import { Bot, Player } from "mineflayer";

function escapeRegex(s: string) {
    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

export class Command {
    readonly pattern: RegExp;
    readonly description: string;
    public readonly action: (
        player: Player | undefined,
        match: RegExpExecArray
    ) => void;

    public constructor(
        pattern: string,
        action: (player: Player | undefined) => void
    );
    public constructor(
        pattern: RegExp,
        description: string,
        action: (
            player: Player | undefined,
            match: RegExpExecArray
        ) => void
    );

    constructor(
        pattern: string | RegExp,
        arg2: unknown,
        arg3?: (
            player: Player | undefined,
            match: RegExpExecArray
        ) => void
    ) {
        if (typeof pattern === "string") {
            this.pattern = new RegExp(`^${escapeRegex(pattern)}\$`);
            this.description = pattern;
            this.action = arg2 as () => void;
        } else {
            this.pattern = pattern;
            this.description = arg2 as string;
            this.action = arg3!;
        }
    }

    matches(s: string) {
        return this.pattern.exec(s);
    }
}

export function chatDispatch(bot: Bot, commands: Command[]) {
    bot.once("spawn", () => {
        const chatStart = `${bot.username}, `;

        bot.on("chat", (username, message) => {
            if (username === bot.username) return;

            message =
                /^\s*(.*?)[\s,.?!]*$/s
                    .exec(message)![1]
                    .toLowerCase();
            if (!message.startsWith(chatStart)) return;
            const content = message.substring(chatStart.length);

            if (content === "what can you do") {
                for (const command of commands) {
                    bot.chat(`I can ${command.description}`);
                }
                return;
            }

            const player: Player | undefined = bot.players[username];
            for (const command of commands) {
                const match = command.matches(content);
                if (match) {
                    return command.action(player, match);
                }
            }
        });
    });
}